/*
	This script file is part of the Sonic 3 A.I.R. script software distribution.
	Copyright (C) 2017-2024 by Eukaryot

	Published under the GNU GPLv3 open source software license, see license.txt
	or https://www.gnu.org/licenses/gpl-3.0.en.html
*/



//-------------------------------------------------------//
// LBZ 1 Boss                                            //
//-------------------------------------------------------//

//# address-hook(0x0723e0) end(0x0723ee)
function void fn0723e0()
{
	// Targets:
	//  - 0x072400	-> objA0.base_state = 0x00
	//  - 0x072452	-> objA0.base_state = 0x02
	//  - 0x072474	-> objA0.base_state = 0x04
	//  - 0x0724bc	-> objA0.base_state = 0x06
	//  - 0x0724e2	-> objA0.base_state = 0x08
	//  - 0x072558	-> objA0.base_state = 0x0a
	call tableLookupAddress(0x0723f4, objA0.base_state)

	Enemy.DrawDynamicObject()
}


//# address-hook(0x072400) end(0x07244c)
function void fn072400()
{
	setupObjectAttributesFull(0x072956)
	boss.remaining_hits = 6
	level.boss_encounter = 1
	u16[A0 + 0x3c] = objA0.position.x.u16
	objA0.countdown_value = 0x10
	objA0.countdown_callback = 0x072458

	loadPaletteLine1(0x07299a)

	// "spawnChildObjects(0x07296e)" replaced by:
	spawnChildObject(0x072596, 0x00, 0, 0)

	// "spawnChildObjectChain(0x072976)" replaced by:
	spawnChildObjectsChain(0x0725f4, 6)		// Bottom arm

	// "spawnChildObjectChain(0x07297c)" replaced by:
	spawnChildObjectsChain(0x0725ee, 6)		// Top arm
}


//# address-hook(0x072452) end(0x072452)
function void fn072452()
{
	Object.CountdownAndTrigger()
}


//# address-hook(0x072474) end(0x07247a)
function void fn072474()
{
	UpdateMovementStraightSimple()
	Object.CountdownAndTrigger()
}


//# address-hook(0x0724bc) end(0x0724c2)
function void fn0724bc()
{
	Object.AnimationProgressByLocalA1()
	Object.CountdownAndTrigger()
}


//# address-hook(0x0724e2) end(0x07254e)
function void fn0724e2()
{
	A1 = 0xffffb000
	D0 = 0
	D1 = 0
	D2.u16 = objA0.position.x.u16
	if (isMainCharacter(CHARACTER_KNUCKLES))
	{
		D4 = 0x20
		if (objA0.subtype2c != 0)
		{
			D4.s16 = -D4.s16
		}
		D2.u16 += D4.u16
	}

	D2.u16 -= objA1.position.x.u16
	if (D2.s16 < 0)
	{
		D2.s16 = -D2.s16
		D0.u16 += 2
	}

	D1 = 0
	D4.u16 = objA1.position.y.u16 - 0x38
	D3.u16 = objA0.position.y.u16 - D4.u16
	if (D3.s16 < 0)
	{
		D3.s16 = -D3.s16
		D1.u16 += 2
	}

	objA0.velocity.x = u16[0x072554 + D0.u16]
	objA0.velocity.y = u16[0x072554 + D1.u16]
	if (D2.u16 <= 4)
	{
		objA0.velocity.x = 0
	}
	if (D3.u16 <= 4)
	{
		objA0.velocity.y = 0
	}

	UpdateMovementStraightSimple()
	Object.AnimationProgressByLocalA1()
	fn072840()
}


//# address-hook(0x072558) end(0x07255c)
function void fn072558()
{
	objA0.position.y.u16 -= 2
	fn072840()
}


//# address-hook(0x072458) end(0x072472)
function void fn072458()
{
	objA0.base_state = 0x04
	objA0.velocity.y = 0
	objA0.countdown_value = 0x3f
	objA0.countdown_callback = 0x072480
}


//# address-hook(0x072480) end(0x0724ba)
function void fn072480()
{
	objA0.base_state = 0x06
	objA0.flags38 |= 0x02
	A1 = 0x072982
	A2 = 0xfffffa82
	D6 = 5
	while (D6.s16 >= 0)
	{
		u8[(A2+=1)-1] = u8[(A1+=1)-1]
		--D6.s16
	}

	u32[A0 + 0x30] = 0xfffffa82
	objA0.animation.timer = 0x10
	objA0.countdown_callback = 0x0724c8
	objA0.countdown_value = 0x10
}


//# address-hook(0x0724b4)
function void fn0724b4()
{
	objA0.countdown_value = 0x10
}


//# address-hook(0x0724c8) end(0x0724e0)
function void fn0724c8()
{
	A1 = 0xfffffa82
	--u8[A1]
	if (u8[A1] > 0x02)
	{
		fn0724b4()
	}
	else
	{
		objA0.base_state = 0x08
		objA0.collision_attributes = collision.size.16x16
	}
}


//# address-hook(0x072596) end(0x0725aa)
function void fn072596()
{
	MoveAndFlipWithParent()

	// Targets:
	//  - 0x0725b6	-> objA0.base_state = 0x00
	//  - 0x0725ca	-> objA0.base_state = 0x02
	//  - 0x0725e8	-> objA0.base_state = 0x04
	call tableLookupAddress(0x0725b0, objA0.base_state)

	fn08498c()
}


//# address-hook(0x0725b6) end(0x0725c8)
function void fn0725b6()
{
	setupObjectAttributesBasic(0x072962)
	u32[A0 + 0x30] = 0x072988
}


//# address-hook(0x0725ca) end(0x0725e6)
function void fn0725ca()
{
	A1 = 0xffff0000 + u16[A0 + 0x46]
	if (u8[A1 + 0x38] & 0x02)
	{
		objA0.base_state = 0x04
		u32[A0 + 0x30] = 0x072988
	}
}


//# address-hook(0x0725e8) end(0x0725e8)
function void fn0725e8()
{
	Object.AnimationProgressByLocalA1()
}


//# address-hook(0x0725ee) end(0x0725ee)
function void fn0725ee()
{
	objA0.flags38 |= 0x04
	fn0725f4()
}


//# address-hook(0x0725f4) end(0x07260c)
function void fn0725f4()
{
	// Targets:
	//  - 0x07261c	-> objA0.base_state = 0x00
	//  - 0x072658	-> objA0.base_state = 0x02
	//  - 0x072688	-> objA0.base_state = 0x04
	//  - 0x0726e4	-> objA0.base_state = 0x06
	//  - 0x07270e	-> objA0.base_state = 0x08
	//  - 0x07278e	-> objA0.base_state = 0x0a
	call tableLookupAddress(0x072610, objA0.base_state)

	if (objA0.subtype2c == 0)
	{
		fn0728c8()
	}
	else
	{
		fn072902()
	}
}


//# address-hook(0x07261c) end(0x072654)
function void fn07261c()
{
	setupObjectAttributesBasic(0x072968)
	objA0.countdown_value = 0x0100
	objA0.countdown_callback = 0x072674
	u8[A0 + 0x3d] = 0x04
	objA0.value3e = 0x05
	if (objA0.subtype2c == 0x0a)
	{
		objA0.value3e = 0x04
		objA0.flags38 |= 0x02
	}
	fn0727b0()
}


//# address-hook(0x0727b0) end(0x0727dc)
function void fn0727b0()
{
	D0 = objA0.subtype2c
	objA0.sprite_priority = u16[0x0727e2 + D0.u16]
	D0.u16 >>= 1
	objA0.animation.sprite = u8[0x0727ee + D0.u16]
	u8[A0 + 0x3c] = u8[0x0727f4 + D0.u16]
	if (objA0.flags38 & 0x04)
	{
		u8[A0 + 0x3c] = u8[0x072800 + D0.u16]
	}
	D2.u16 = objA0.value3e
	fn084c42()
}


//# address-hook(0x072658) end(0x07266e)
function void fn072658()
{
	if (objA0.subtype2c == 0)
	{
		MoveAndFlipWithParent()
	}
	D2.u16 = objA0.value3e
	fn084c42()
	Object.CountdownAndTrigger()
}


//# address-hook(0x072674) end(0x072686)
function void fn072674()
{
	objA0.base_state = 0x04
	fn07280c()
	objA0.countdown_callback = 0x0726b2
}


//# address-hook(0x072688) end(0x0726ac)
function void fn072688()
{
	if (objA0.flags38 & 0x02)
	{
		D0.u8 = u8[A0 + 0x3d]
		u8[A0 + 0x3c] += D0.u8
		D2.u16 = objA0.value3e
		fn084c42()
		Object.CountdownAndTrigger()
	}
	else
	{
		D2.u16 = objA0.value3e
		fn084c42()
	}
}


//# address-hook(0x0726b2) end(0x0726e2)
function void fn0726b2()
{
	fn072826()
	objA0.countdown_callback = 0x072726
	if (objA0.subtype2c != 0)
	{
		objA0.base_state = 0x06
		A1 = 0xffff0000 + u16[A0 + 0x46]
		u8[A1 + 0x38] |= 0x02
		u8[A0 + 0x3c] = u8[A1 + 0x3c]
	}
	else
	{
		objA0.base_state = 0x08
	}
}


//# address-hook(0x0726e4) end(0x07270c)
function void fn0726e4()
{
	A1 = 0xffff0000 + u16[A0 + 0x46]
	u8[A0 + 0x3c] = u8[A1 + 0x3c]
	D2.u16 = objA0.value3e
	fn084c42()

	A1 = 0xffff0000 + u16[A0 + 0x46]
	if ((u8[A1 + 0x38] & 0x02) == 0)
	{
		objA0.base_state = 0x08
	}
}


//# address-hook(0x07270e) end(0x072720)
function void fn07270e()
{
	D0.u8 = u8[A0 + 0x3d]
	u8[A0 + 0x3c] += D0.u8
	Object.CountdownAndTrigger()
	D2.u16 = objA0.value3e
	fn084c42()
}


//# address-hook(0x072726) end(0x07277c)
function void fn072726()
{
	objA0.flags38 &= ~0x02
	s8[A0 + 0x3d] = -s8[A0 + 0x3d]
	fn072674()
	if (objA0.subtype2c == 0x0a)
	{
		objA0.base_state = 0x0a
		objA0.countdown_value = 0x3c
		objA0.countdown_callback = 0x072674
		objA0.flags38 |= 0x02
	}

	D0 = objA0.subtype2c >> 1
	D1 = 0
	if (objA0.flags38 & 0x04)
	{
		D1.u16 += 8
	}
	if (s8[A0 + 0x3d] < 0)
	{
		D1.u16 += 4
	}
	A1 = u32[0x07277e + D1.u16]
	u8[A0 + 0x3c] = u8[A1 + D0.u16]
}


//# address-hook(0x07278e) end(0x072798)
function void fn07278e()
{
	D2.u16 = objA0.value3e
	fn084c42()
	Object.CountdownAndTrigger()
}


//# address-hook(0x07280c) end(0x072818)
function void fn07280c()
{
	objA0.countdown_value = u16[0x07281a + objA0.subtype2c]
}


//# address-hook(0x072826) end(0x072832)
function void fn072826()
{
	objA0.countdown_value = u16[0x072834 + objA0.subtype2c]
}


//# address-hook(0x072840) end(0x0728c2)
function void fn072840()
{
	if (u8[A0 + 0x28] == 0)
	{
		if (boss.remaining_hits == 0)
		{
			objA0.update_address = 0x085694
			objA0.countdown_callback = 0x072562

			// "spawnChildObjects(0x083fee)" replaced by:
			spawnChildObject(0x083d84, 0x00, 0, 0)		// Boss explosion generator

			if (!isMainCharacter(CHARACTER_KNUCKLES))
			{
				AddScoreForBossEnemy()
			}
			else
			{
				AddScoreForBossEnemy_NoHudDirty()
			}
			return
		}

		if (boss.remaining_hits == 0x03)
		{
			objA0.flags38 |= 0x01
		}

		if (objA0.state == 0)
		{
			objA0.value3a = objA0.base_state
			objA0.base_state = 0x0a
			objA0.state = 0x20
			playSound(0x6e)
		}

		fn084f92()

		--objA0.state
		if (objA0.state == 0)
		{
			objA0.base_state = objA0.value3a
			u16[0xfffffc22] = 0x0eee
			u8[A0 + 0x28] = u8[A0 + 0x25]
		}
	}
}


//# address-hook(0x0728c8) end(0x0728e4)
function void fn0728c8()
{
	A1 = 0xffff0000 + u16[A0 + 0x46]
	if ((u8[A1 + 0x2a] & 0x80) == 0)
	{
		if ((u8[A1 + 0x38] & 0x01) == 0 || (objA0.flags38 & 0x04) == 0)
		{
			Enemy.DrawDynamicObject()
			return
		}
	}
	fn0728ea()
}


//# address-hook(0x0728ea) end(0x0728fc)
function void fn0728ea()
{
	D0.u16 = u16[A0 + 0x44]
	if (D0.u16 != 0)
	{
		A1 = 0xffff0000 + D0.u16
		u8[A1 + 0x2a] |= 0x80
	}
	fn072910()
	Enemy.DrawDynamicObject()
}


//# address-hook(0x072902) end(0x07290a)
function void fn072902()
{
	if (objA0.flags2a & 0x80)
	{
		fn0728ea()
	}
	else
	{
		Enemy.DrawDynamicObject()
	}
}


//# address-hook(0x072910) end(0x07293c)
function void fn072910()
{
	objA0.update_address = 0x07279e
	u8[A0 + 0x28] = 0
	objA0.countdown_value = 0x80
	objA0.countdown_callback = addressof(Object.TriggerUnloading)

	D0 = objA0.subtype2c * 2
	A1 = 0x07293e + D0.u16
	objA0.velocity.x = u16[(A1+=2)-2]
	objA0.velocity.y = u16[(A1+=2)-2]
}


//# address-hook(0x07279e) end(0x0727aa)
function void fn07279e()
{
	UpdateMovementSimple()
	Object.CountdownAndTrigger()
	DrawObject()
}



//# address-hook(0x084f92) end(0x084fa2)
function void fn084f92()
{
	// Boss damage palette effect
#if STANDALONE
	// Flash originally affects the wrong color, fix #contributed by Legobouwer
	// TODO: Also add support for anti-flicker setting
	A1 = 0xfffffc36
	u16[A1] = (u16[A1] == 0x0222) ? 0x0eee : 0x0222
#else
	A1 = 0xfffffc22
	u16[A1] = (u16[A1] == 0) ? 0x0eee : 0
#endif
}


//# address-hook(0x072562) end(0x072590)
function void fn072562()
{
	if (!isMainCharacter(CHARACTER_KNUCKLES))
	{
		if (allocDynamicObjectStd())
		{
			// Create the object that fades out the boss theme and starts the level music afterwards
			objA1.update_address = 0x085b0a
		}
		objA0.position.x.u16 = u16[A0 + 0x3c]
		fn085ba8()
	}
	else
	{
		A1 = 0xffff0000 + u16[A0 + 0x46]
		D0.u8 = objA0.subtype2c
		u8[A1 + 0x38] |= (1 << D0.u8)
		fn0852ae()
	}
}



// For Knuckles

//# address-hook(0x08cf80) end(0x08cfac)
function void fn08cf80()
{
	A1 = 0x08cf70
	if (InitBoss(0x08cf8a))
		return

	level.vertical_wrap = u16[(A1+=2)-2]
	move_area.bottom.current = u16[A1]
	move_area.bottom.target = u16[(A1+=2)-2]
	move_area.left = u16[(A1+=2)-2]
	move_area.right = u16[(A1+=2)-2]
	fn08d0ea()

	// "spawnChildObjects(0x08d26c)" replaced by:
	spawnChildObject(0x08d046, 0x00, -32, 0)
	spawnChildObject(0x08d046, 0x02, 32, 0)

	objA0.update_address = 0x08cfb2
	fn08cfb2()
}


//# address-hook(0x08cfb2) end(0x08cfc6)
function void fn08cfb2()
{
	if (camera.position.y.u16 >= 0x0932)
	{
		objA0.update_address = 0x08cfc8
		objA0.flags38 |= 0x02
	}
}


//# address-hook(0x08cfc8) end(0x08d012)
function void fn08cfc8()
{
	D0.u8 = objA0.flags38 & 0x05
	if (D0.u8 != 0x05)
		return

	objA0.update_address = 0x08d018
	objA0.countdown_value = 0x1f
	D0.u16 = 0x0a80
	screenmover_target.bottom = D0.u16
	move_area.bottom.target = D0.u16

#if !STANDALONE
	// Do not create this object, it leads to the level music starting twice
	if (allocDynamicObjectStd())
	{
		// Create the object that fades out the boss theme and starts the level music afterwards
		objA1.update_address = 0x085b0a
	}
#endif
	hud.dirty.timer = 0

	// "spawnSimpleChildObjects(0x084b12)" replaced by:
	spawnSimpleChildObjects(0x084ad2, 1)		// Screen mover for the lower border

	// "spawnSimpleChildObjects(0x08d27a)" replaced by:
	spawnSimpleChildObjects(0x08d0ae, 5)
}


//# address-hook(0x08d018) end(0x08d02c)
function void fn08d018()
{
	--objA0.countdown_value
	if (objA0.countdown_value < 0)
	{
		objA0.update_address = 0x08d02e
		u16[0xffffeec4] = 0x55
	}
}


//# address-hook(0x08d02e) end(0x08d044)
function void fn08d02e()
{
	if (camera.position.y.u16 >= 0x0a7c)
	{
	#if STANDALONE
		// Move object a bit left, so the signpost gets spawned in a better location
		objA0.position.x.u16 -= 0x10
	#endif

		objA0.update_address = 0x085668
		objA0.countdown_callback = 0x085ba8
	}
}


//# address-hook(0x08d046) end(0x08d050)
function void fn08d046()
{
	objA0.update_address = 0x08d056

	// "spawnIdenticalChildObjects(0x08d25c)" replaced by:
	spawnChildObjects(0x08ce34, 0, 0, 10)
}


//# address-hook(0x08d056) end(0x08d080)
function void fn08d056()
{
	A1 = 0xffff0000 + u16[A0 + 0x46]
	if (u8[A1 + 0x38] & 0x02)
	{
		objA0.update_address = addressof(Object.CountdownAndTrigger)
		objA0.countdown_value = 119
		objA0.countdown_callback = 0x08d082
		if (objA0.subtype2c == 0)
		{
			fn08d116()
		}
	}
}


//# address-hook(0x08d082) end(0x08d0aa)
function void fn08d082()
{
	objA0.flags38 |= 0x08
	playSound(0x50)

	// "spawnChildObjects(0x08d264)" replaced by:
	spawnChildObject(0x0723e0, 0x00, 0, 0)
	if (_equal())
	{
		A2 = 0xffff0000 + u16[A0 + 0x46]
		u16[A1 + 0x46] = A2.u16
		u8[A1 + 0x2c] = objA0.subtype2c
	}
	fn0852ae()
}


//# address-hook(0x08d0ae) end(0x08d0da)
function void fn08d0ae()
{
	objA0.update_address = addressof(Object.CountdownAndTrigger)
	objA0.countdown_value = 0xff
	objA0.countdown_callback = addressof(Object.TriggerUnloading)
	objA0.position.x.u16 = u16[0x08d0e0 + objA0.subtype2c]
	objA0.position.y.u16 = 0x0a20

	// "spawnChildObjects(0x083fee)" replaced by:
	spawnChildObject(0x083d84, 0x00, 0, 0)		// Boss explosion generator
}
